#include "common/common.h"
#include "string/stringoutput.h"
#include "Core/CAbstractThread.h"
#include "testlibpilo.h"

using namespace PILO::Core::Thread;

static INT32 g_id = 0;

M_ATOMIC INT8 g_check8 = 0;
M_ATOMIC INT16 g_check16 = 0;
M_ATOMIC INT32 g_check32 = 0;
M_ATOMIC INT64 g_check64 = 0;
M_ATOMIC UINT8 g_check8u = 0;
M_ATOMIC UINT16 g_check16u = 0;
M_ATOMIC UINT32 g_check32u = 0;
M_ATOMIC UINT64 g_check64u = 0;


#ifdef  WINDOWS
//8bit int test
static THREAD_ROUTINE_RETURN_TYPE THREAD_ROUTINE_CALL atomicAddInt8(void *)
{
    int i=0;
    for(i=0;i< 10;++i){
        AtomicIntegerFetchAdd32(&g_check8,1);
    }
    _endthreadex( 0 );
    return 0;
}
static INT32 test_atomic_int8_adding(void* )
{
    HANDLE  hdl[10];
    int i;
    for( i=0;i<10;++i){
        hdl[i] = (HANDLE) _beginthreadex(0,0, atomicAddInt8,0,0,0);
        if  (hdl[i] == (HANDLE) -1) {
            return -1;
        }
    }

    int total = 10;
    while(total > 0){
        int rc = WaitForMultipleObjects(total, hdl, FALSE, INFINITE);
        if (rc == WAIT_FAILED) {
            return -1;

        } else if (rc >= WAIT_OBJECT_0 && rc <= total-1 ) {
            CloseHandle(hdl[rc - WAIT_OBJECT_0]);
            int index = rc - WAIT_OBJECT_0;
            memmove(hdl+index,hdl+index+1,  (total-index-1) * sizeof(HANDLE));
            total --;
        }
    }
    if (100 != g_check8) {
        return -2;
    }

    return 0;
}

//8bit uint test
static THREAD_ROUTINE_RETURN_TYPE THREAD_ROUTINE_CALL atomicAddUInt8(void *)
{
    int i=0;
    for(i=0;i< 20;++i){
        AtomicIntegerFetchAdd32(&g_check8u,1);
    }
    _endthreadex( 0 );
    return 0;
}
static INT32 test_atomic_uint8_adding(void* )
{
    HANDLE  hdl[10];
    int i;
    for( i=0;i<10;++i){
        hdl[i] = (HANDLE) _beginthreadex(0,0, atomicAddUInt8,0,0,0);
        if  (hdl[i] == (HANDLE) -1) {
            return -1;
        }
    }

    int total = 10;
    while(total > 0){
        int rc = WaitForMultipleObjects(total, hdl, FALSE, INFINITE);
        if (rc == WAIT_FAILED) {
            return -1;

        } else if (rc >= WAIT_OBJECT_0 && rc <= total-1 ) {
            CloseHandle(hdl[rc - WAIT_OBJECT_0]);
            int index = rc - WAIT_OBJECT_0;
            memmove(hdl+index,hdl+index+1,  (total-index-1) * sizeof(HANDLE));
            total --;
        }
    }
    if (200 != g_check8u) {
        return -2;
    }

    return 0;
}

//16bit int test
static THREAD_ROUTINE_RETURN_TYPE THREAD_ROUTINE_CALL atomicAddInt16(void *)
{
    int i=0;
    for(i=0;i<  1000;++i){
        AtomicIntegerFetchAdd32(&g_check16,1);
    }
    _endthreadex( 0 );
    return 0;
}
static INT32 test_atomic_int16_adding(void* )
{
    HANDLE  hdl[20];
    int i;
    for( i=0;i<20;++i){
        hdl[i] = (HANDLE) _beginthreadex(0,0, atomicAddInt16,0,0,0);
        if  (hdl[i] == (HANDLE) -1) {
            return -1;
        }
    }

    int total = 20;
    while(total > 0){
        int rc = WaitForMultipleObjects(total, hdl, FALSE, INFINITE);
        if (rc == WAIT_FAILED) {
            return -1;

        } else if (rc >= WAIT_OBJECT_0 && rc <= total-1 ) {
            CloseHandle(hdl[rc - WAIT_OBJECT_0]);
            int index = rc - WAIT_OBJECT_0;
            memmove(hdl+index,hdl+index+1,  (total-index-1) * sizeof(HANDLE));
            total --;
        }
    }

    if (20000 != g_check16) {
        return -2;
    }

    return 0;
}

//16bit uint test
static THREAD_ROUTINE_RETURN_TYPE THREAD_ROUTINE_CALL atomicAddUInt16(void *)
{
    int i=0;
    for(i=0;i<  3000;++i){
        AtomicIntegerFetchAdd32(&g_check16u,1);
    }
    _endthreadex( 0 );
    return 0;
}
static INT32 test_atomic_uint16_adding(void* )
{
    HANDLE  hdl[20];
    int i;
    for( i=0;i<20;++i){
        hdl[i] = (HANDLE) _beginthreadex(0,0, atomicAddUInt16,0,0,0);
        if  (hdl[i] == (HANDLE) -1) {
            return -1;
        }
    }

    int total = 20;
    while(total > 0){
        int rc = WaitForMultipleObjects(total, hdl, FALSE, INFINITE);
        if (rc == WAIT_FAILED) {
            return -1;

        } else if (rc >= WAIT_OBJECT_0 && rc <= total-1 ) {
            CloseHandle(hdl[rc - WAIT_OBJECT_0]);
            int index = rc - WAIT_OBJECT_0;
            memmove(hdl+index,hdl+index+1,  (total-index-1) * sizeof(HANDLE));
            total --;
        }
    }

    if (60000 != g_check16u) {
        return -2;
    }

    return 0;
}

//32bit int test
static THREAD_ROUTINE_RETURN_TYPE THREAD_ROUTINE_CALL atomicAddInt32(void *)
{
    int i=0;
    for(i=0;i<  1000000;++i){
        AtomicIntegerFetchAdd32(&g_check32,100);
    }
    _endthreadex( 0 );
    return 0;
}
static INT32 test_atomic_int32_adding(void* )
{
    HANDLE  hdl[20];
    int i;
    for( i=0;i<20;++i){
        hdl[i] = (HANDLE) _beginthreadex(0,0, atomicAddInt32,0,0,0);
        if  (hdl[i] == (HANDLE) -1) {
            return -1;
        }
    }

    int total = 20;
    while(total > 0){
        int rc = WaitForMultipleObjects(total, hdl, FALSE, INFINITE);
        if (rc == WAIT_FAILED) {
            return -1;

        } else if (rc >= WAIT_OBJECT_0 && rc <= total-1 ) {
            CloseHandle(hdl[rc - WAIT_OBJECT_0]);
            int index = rc - WAIT_OBJECT_0;
            memmove(hdl+index,hdl+index+1,  (total-index-1) * sizeof(HANDLE));
            total --;
        }
    }

    if (2000000000 != g_check32) {
        return -2;
    }

    return 0;
}

//32bit uint test
static THREAD_ROUTINE_RETURN_TYPE THREAD_ROUTINE_CALL atomicAddUInt32(void *)
{
    int i=0;
    for(i=0;i< 2000000;++i){
        AtomicIntegerFetchAdd32(&g_check32u,100);
    }
    _endthreadex( 0 );
    return 0;
}
static INT32 test_atomic_uint32_adding(void* )
{
    HANDLE  hdl[20];
    int i;
    for( i=0;i<20;++i){
        hdl[i] = (HANDLE) _beginthreadex(0,0, atomicAddUInt32,0,0,0);
        if  (hdl[i] == (HANDLE) -1) {
            return -1;
        }
    }

    int total = 20;
    while(total > 0){
        int rc = WaitForMultipleObjects(total, hdl, FALSE, INFINITE);
        if (rc == WAIT_FAILED) {
            return -1;

        } else if (rc >= WAIT_OBJECT_0 && rc <= total-1 ) {
            CloseHandle(hdl[rc - WAIT_OBJECT_0]);
            int index = rc - WAIT_OBJECT_0;
            memmove(hdl+index,hdl+index+1,  (total-index-1) * sizeof(HANDLE));
            total --;
        }
    }

    if (4000000000U != g_check32u) {
        return -2;
    }

    return 0;
}

//64bit int test
static THREAD_ROUTINE_RETURN_TYPE THREAD_ROUTINE_CALL atomicAddInt64(void *)
{

    int i=0;
    for(i=0;i<  10000000;++i){
        AtomicIntegerFetchAdd64(&g_check64,100);
    }
    _endthreadex( 0 );
    return 0;
}

static INT32 test_atomic_int64_adding(void* )
{
    HANDLE  hdl[20];

    int i;
    for( i=0;i<20;++i){
        hdl[i] = (HANDLE) _beginthreadex(0,0, atomicAddInt64,0,0,0);
        if  (hdl[i] == (HANDLE) -1) {
            return -1;
        }
    }

    int total = 20;
    while(total > 0){
        int rc = WaitForMultipleObjects(total, hdl, FALSE, INFINITE);
        if (rc == WAIT_FAILED) {
            return -1;

        } else if (rc >= WAIT_OBJECT_0 && rc <= total-1 ) {
            int index = rc - WAIT_OBJECT_0;
            CloseHandle(hdl[index]);
            memmove(hdl+index,hdl+index+1,  (total-index-1) * sizeof(HANDLE));
            total --;
        }
    }


    if ((INT64)20000000000 != g_check64) {
        return -2;
    }

    return 0;
}

//64bit uint test
static THREAD_ROUTINE_RETURN_TYPE THREAD_ROUTINE_CALL atomicAddUInt64(void *)
{

    int i=0;
    for(i=0;i<  20000000;++i){
        AtomicIntegerFetchAdd64(&g_check64u,100);
    }
    _endthreadex( 0 );
    return 0;
}

static INT32 test_atomic_uint64_adding(void* )
{
    HANDLE  hdl[20];

    int i;
    for( i=0;i<20;++i){
        hdl[i] = (HANDLE) _beginthreadex(0,0, atomicAddUInt64,0,0,0);
        if  (hdl[i] == (HANDLE) -1) {
            return -1;
        }
    }

    int total = 20;
    while(total > 0){
        int rc = WaitForMultipleObjects(total, hdl, FALSE, INFINITE);
        if (rc == WAIT_FAILED) {
            return -1;

        } else if (rc >= WAIT_OBJECT_0 && rc <= total-1 ) {
            int index = rc - WAIT_OBJECT_0;
            CloseHandle(hdl[index]);
            memmove(hdl+index,hdl+index+1,  (total-index-1) * sizeof(HANDLE));
            total --;
        }
    }


    if (40000000000ULL != g_check64u) {
        return -2;
    }

    return 0;
}






#else
#include <pthread.h>
//8bit int test
static void* atomicAddInt8(void *)
{
    int i=0;
    for(i=0;i< 10;++i){
        AtomicIntegerFetchAdd32(&g_check8,1);
    }
    return 0;
}
static INT32 test_atomic_int8_adding(void* )
{

    pthread_t  tid[10];
    int i;
    for( i=0;i<10;++i){
        if (0 != pthread_create(&tid[i], 0, atomicAddInt8,0)) {
            return -1;
        }
    }

    for (i=0; i<10; ++i) {
        pthread_join(tid[i], 0);
    }


    if (100 != g_check8) {
        return -2;
    }

    return 0;
}

//8bit uint test
static void* atomicAddUInt8(void *)
{
    int i=0;
    for(i=0;i< 20;++i){
        AtomicIntegerFetchAdd32(&g_check8u,1);
    }
    return 0;
}
static INT32 test_atomic_uint8_adding(void* )
{

    pthread_t  tid[10];
    int i;
    for( i=0;i<10;++i){
        if (0 != pthread_create(&tid[i], 0, atomicAddUInt8,0)) {
            return -1;
        }
    }

    for (i=0; i<10; ++i) {
        pthread_join(tid[i], 0);
    }


    if (200 != g_check8u) {
        return -2;
    }

    return 0;
}

//16bit int test
static void* atomicAddInt16(void *)
{
    int i=0;
    for(i=0;i< 1000;++i){
        AtomicIntegerFetchAdd32(&g_check16,1);
    }
    return 0;
}
static INT32 test_atomic_int16_adding(void* )
{

    pthread_t  tid[20];
    int i;
    for( i=0;i<20;++i){
        if (0 != pthread_create(&tid[i], 0, atomicAddInt16,0)) {
            return -1;
        }
    }

    for (i=0; i<20; ++i) {
        pthread_join(tid[i], 0);
    }


    if (20000 != g_check16) {
        return -2;
    }

    return 0;
}

//16bit uint test
static void* atomicAddUInt16(void *)
{
    int i=0;
    for(i=0;i< 3000;++i){
        AtomicIntegerFetchAdd32(&g_check16u,1);
    }
    return 0;
}
static INT32 test_atomic_uint16_adding(void* )
{

    pthread_t  tid[20];
    int i;
    for( i=0;i<20;++i){
        if (0 != pthread_create(&tid[i], 0, atomicAddUInt16,0)) {
            return -1;
        }
    }

    for (i=0; i<20; ++i) {
        pthread_join(tid[i], 0);
    }


    if (60000 != g_check16u) {
        return -2;
    }

    return 0;
}

//32bit int test
static void* atomicAddInt32(void *)
{

    int i=0;
    for(i=0;i<  1000000;++i){
        AtomicIntegerFetchAdd32(&g_check32,100);
    }
    return 0;
}
static INT32 test_atomic_int32_adding(void* )
{

    pthread_t  tid[20];

    int i;
    for( i=0;i<20;++i){
        if (0 != pthread_create(&tid[i], 0, atomicAddInt32,0)) {
            return -1;
        }
    }

    for (i=0; i<20; ++i) {
        pthread_join(tid[i], 0);
    }


    if (2000000000 != g_check32) {
        return -2;
    }

    return 0;
}

//32bit uint test
static void* atomicAddUInt32(void *)
{

    int i=0;
    for(i=0;i<  2000000;++i){
        AtomicIntegerFetchAdd32(&g_check32u,100);
    }
    return 0;
}
static INT32 test_atomic_uint32_adding(void* )
{

    pthread_t  tid[20];

    int i;
    for( i=0;i<20;++i){
        if (0 != pthread_create(&tid[i], 0, atomicAddUInt32,0)) {
            return -1;
        }
    }

    for (i=0; i<20; ++i) {
        pthread_join(tid[i], 0);
    }


    if (4000000000U != g_check32u) {
        return -2;
    }

    return 0;
}

//64bit int test
static void* atomicAddInt64(void *)
{

    int i=0;
    for(i=0;i<  10000000;++i){
        AtomicIntegerFetchAdd64(&g_check64,100);
    }
    return 0;
}
static INT32 test_atomic_int64_adding(void* )
{

    pthread_t  tid[20];

    int i;
    for( i=0;i<20;++i){
        if (0 != pthread_create(&tid[i], 0, atomicAddInt64,0)) {
            return -1;
        }
    }

    for (i=0; i<20; ++i) {
        pthread_join(tid[i], 0);
    }


    if ((INT64) 20000000000 != g_check64) {
        return -2;
    }

    return 0;
}

//64bit uint test
static void* atomicAddUInt64(void *)
{

    int i=0;
    for(i=0;i<  20000000;++i){
        AtomicIntegerFetchAdd64(&g_check64u,100);
    }
    return 0;
}
static INT32 test_atomic_int64_adding(void* )
{

    pthread_t  tid[20];

    int i;
    for( i=0;i<20;++i){
        if (0 != pthread_create(&tid[i], 0, atomicAddUInt64,0)) {
            return -1;
        }
    }

    for (i=0; i<20; ++i) {
        pthread_join(tid[i], 0);
    }


    if ((UINT64) 40000000000ULL != g_check64u) {
        return -2;
    }

    return 0;
}



#endif

static INT32 test_atomic_bitop(void* )
{
    UINT8    uc = 159;
    UINT8    ander = 173;
    UINT8    ucold= uc;
    if (ucold!= (UINT8) AtomicIntegerAnd8(&uc, ander)) {
        return -1;
    }
    if (uc != (159 & 173)) {
        return -2;
    }

    UINT16    us = 45123;
    if (45123 != (UINT16)AtomicIntegerAnd16(&us, 12345)) {
        return -3;
    }
    if (us != (UINT32)(45123 & 12345)) {
        return -4;
    }

    UINT32    ui = 2109027824;
    if (2109027824 != (UINT32)AtomicIntegerAnd32(&ui, 22340954)) {
        return -5;
    }
    if (ui != (UINT32) (2109027824 & 22340954)) {
        return -6;
    }

#if defined(_WIN64)
    UINT64    ull = 2109027824899123ULL;
    if (2109027824899123ULL != AtomicIntegerAnd64(&ull, 72102345899123ULL)) {
        return -7;
    }
    if (ull != (UINT64) (72102345899123ULL & 2109027824899123ULL)) {
        return -8;
    }
#endif

    return 0;
}

INT32 test_atomic_assign(void* )
{
    UINT32    ui = 1599267213;
    if (1599267213 !=  AtomicIntegerSet32(&ui, -43)) {
        return -1;
    }
    if (ui != (UINT32)(-43)) {
        return -2;
    }

    UINT64    ull = 15992672199993ULL;
    if (15992672199993ULL !=  AtomicIntegerSet64(&ull, -43)) {
        return -3;
    }
    if (ull != (UINT64)(-43)) {
        return -4;
    }

    return 0;
}




//-------------------------------------------------------------------------------------------------------------
static case_testing_t g_cases[] = 
{
     {1, "testing Atomic add of 8bit int                    ",       0,test_atomic_int8_adding,  -1},
     {2, "testing Atomic add of 8bit uint                   ",      0,test_atomic_uint8_adding, -1},
     {3, "testing Atomic add of 16bit int                   ",      0,test_atomic_int16_adding, -1},
     {4, "testing Atomic add of 16bit uint                  ",     0,test_atomic_uint16_adding,-1},
     {5, "testing Atomic add of 32bit int                   ",      0,test_atomic_int32_adding, -1},
     {6, "testing Atomic add of 32bit uint                  ",     0,test_atomic_uint32_adding,-1},
     {7, "testing Atomic add of 64bit int                   ",      0,test_atomic_int64_adding, -1},
     {8, "testing Atomic add of 64bit uint                  ",     0,test_atomic_uint64_adding,-1},
     {9, "testing Atomic bit OP of int                      ",         0,test_atomic_bitop,        -1},
     {10,"testing Atomic assign of int                      ",         0,test_atomic_assign,       -1},


};

INT32 test_atomic_operation_functional(INT32 id, int breakOnErrorOccured)
{
    g_id = id;

    FormatOutputToConsole("Testing Part_%d <atomicoperation.h>\n", id);
    int i=0;
    for (i=0; i<MCF_COUNT_OF(g_cases); i++) {
        case_testing_t * caseParam = &g_cases[i];
        ASSERT(caseParam != NULL);
        FormatOutputToConsole(" #[%03d.%03d]: Testing %s",g_id, caseParam->ct_id, caseParam->ct_decription);

        caseParam->ct_result = caseParam->ct_function(static_cast<case_testing_t *>(caseParam));
        if (caseParam->ct_result == 0) { //ok 
            FormatOutputToConsole("[PASSED]\n");
        } else {
            FormatOutputToConsole("[FAILED:%d]\n",caseParam->ct_result);
            if (breakOnErrorOccured) {
                ASSERT(false);
            }
        }
    }


    FormatOutputToConsole("Result of Part_%d <atomicoperation.h> total %d,   failed %d\n\n",id, MCF_COUNT_OF(g_cases), failedOFCases(g_cases,MCF_COUNT_OF(g_cases) ));
    return 0;
}
